"""
elio框架的常用工具函数。
"""
import socket
from google.protobuf.wrappers_pb2 import BoolValue, BytesValue, DoubleValue, FloatValue, Int32Value, Int64Value, \
    StringValue
from google.protobuf.any_pb2 import Any
from google.protobuf.empty_pb2 import Empty
from ..proto import Interaction  # type: ignore
from typing import Awaitable, Coroutine, Tuple, Type, Union


def to_any(
        val: Union[Awaitable, bool, bytes, Coroutine, float, int, str, None],
        cls: Type[BoolValue | DoubleValue | FloatValue | Int32Value | Int64Value | Interaction | StringValue |
                  Empty] | None = None,
        **kwargs
) -> Union[Any, Empty]:
    """
    把python的数据转换成protobuf的通用类型。
    """
    a = Any()
    if cls == Interaction:
        a.Pack(cls(**kwargs))
        return a
    if cls:
        a.Pack(cls() if cls == Empty
               else cls(value=val))  # type: ignore
        return a
    b = BoolValue(value=val) if isinstance(val, bool) \
        else StringValue(value=val) if isinstance(val, str) \
        else DoubleValue(value=val) if isinstance(val, float) \
        else (Int32Value(value=val) if val.bit_length() < 33 else Int64Value(value=val)) if isinstance(val, int) \
        else BytesValue(value=val) if isinstance(val, bytes) \
        else Empty()
    a.Pack(b)
    return a


def from_any(
        val: Any,
        cls: Type[
                 BoolValue | DoubleValue | FloatValue | Int32Value | Int64Value | Interaction |
                 StringValue | Empty] | Awaitable
) -> Union[bool, bytes, Coroutine, float, int, str, Tuple, None]:
    """
    把protobuf的通用数据转换成python的数据。
    """
    a = cls()
    val.Unpack(a)
    if cls == Interaction:
        return getattr(a, 'name'), getattr(a, 'session'), getattr(a, 'future'), getattr(a, 'argv')
    return getattr(a, 'value')


def convert_type(*type_list) -> list[Type[BoolValue | BytesValue | Empty | Int32Value | Int64Value | StringValue]]:
    """
    python的数据类型映射到protobuf类型。
    """
    cls_list = []
    for item in type_list:
        if item == bool:
            cls_list.append(BoolValue)
        elif item == str:
            cls_list.append(StringValue)
        elif item == float:
            cls_list.append(DoubleValue)
        elif item == int:
            cls_list.append(Int32Value)
        elif item == 'long':
            cls_list.append(Int64Value)
        elif item == bytes:
            cls_list.append(BytesValue)
        elif hasattr(item, '__args__'):
            cls_list.append(convert_type(*item.__args__))
        else:
            cls_list.append(Empty)
    return cls_list


def callback(*type_list):
    """
    把从设备端发送过来的回调参数类型转换成python基本数据类型。
    在回调函数上使用包装器语法。
    """
    cls_list = convert_type(*type_list)

    def wrap(func):
        """
        内部包装器函数。
        """

        def inner(self, *args, **kwargs):
            """
            处理类型转换过程。
            """
            def handle(arg, cls):
                """
                处理转换。
                """
                if isinstance(cls, list):
                    for c in cls:
                        return from_any(arg, c)
                return from_any(arg, cls)
            convert_args = [handle(arg, cls_list[i]) for i, arg in enumerate(args)]
            return func(self, *convert_args, **kwargs)

        return inner

    return wrap


def get_local_ip():
    """
    获取本机IP
    """
    return socket.gethostbyname(socket.gethostname())


if __name__ == '__main__':
    print(to_any(b"abc"))
